package com.cvtt.sip;

import java.text.SimpleDateFormat;
import java.util.Date;

import android.content.Context;
import android.os.PowerManager;
import android.text.TextUtils;
import android.util.Log;

import com.cvtt.common.NetworkUtil;
import com.cvtt.common.Util;
import com.cvtt.voice.VoiceEngine;
import com.zoolu.net.IpAddress;
import com.zoolu.net.NetUtil;
import com.zoolu.net.SocketAddress;
import com.zoolu.sip.dialog.KickOutDialog;
import com.zoolu.sip.dialog.KickOutListener;
import com.zoolu.sip.header.DeviceGuidHeader;
import com.zoolu.sip.message.Message;
import com.zoolu.sip.provider.SipProvider;
import com.zoolu.sip.provider.SipStack;



public class SIPEngine implements RegisterAgentListener, CallAgentListener, KickOutListener {

	private final static int STATE_OFFLINE = 0;
	private final static int STATE_ONLINE = 1;
	private final static int STATE_REGING = 2;
	private final static int STATE_REFRESHING = 3;
	private final static int STATE_UNREGING = 4;
	
	// added by zhh_li 2015-11-30 for kickout
	public final static int STATUS_KICK = 5;

	public final static int CALLSTATE_IDLE = 0;
	public final static int CALLSTATE_INCOMING = 1;
	public final static int CALLSTATE_OUTGOING = 2;
	public final static int CALLSTATE_RINGING = 3;
	public final static int CALLSTATE_ON = 4;
	public final static int CALLSTATE_HOLD = 5;

	private int nState = STATE_OFFLINE;
	private int nCallState = CALLSTATE_IDLE;

	private String strCallNumber;

	private int nCallAgentState;

	private SIPListener sipListener;

	private CallAgent callAgent;
	private RegisterAgent regAgent;

	private SIPConfig sipConfig;

	private SipProvider sipProvider;

	private static SIPEngine sipEngine = null;

	private boolean bStart;

	private VoiceEngine voiceEngine;

	private PowerManager.WakeLock pwLock;

	private StringBuffer reportStrBuffer;
	private SimpleDateFormat mFormatter;
	private Context context;

	private boolean bRetryRDS = true;
	
	private KickOutDialog kickOutDialog;
	
	/**added by zxjh_li 2015-11-27 for kickout  jiekou cjcj */  
	String device_guid = null;
	
	
	
	public static synchronized SIPEngine getInstance(Context ctx, SIPListener listener) {
		if (sipEngine == null) {
			sipEngine = new SIPEngine(ctx, listener);
		}
		return sipEngine;
	}

	public static SIPEngine getSIPEngine() {
		return sipEngine;
	}
	public  boolean setDeviceGuid(String device_guid ) {
		SipStack.device_guid =device_guid ;
		return true;
	}

	private SIPEngine(Context ctx, SIPListener listener) {
		bStart = false;
		context = ctx;
		sipConfig = SIPConfig.getInstance(ctx);
		voiceEngine = VoiceEngine.getInstance(ctx);
		sipListener = listener;
		
		device_guid = Util.makeUUID();
		setDeviceGuid(device_guid);
	}

	public void setListener(SIPListener listener) {
		sipListener = listener;
	}

	public void setContext(Context ctx) {
		sipConfig.context = ctx;
		voiceEngine.setContext(ctx);
	}

	public void setLoginInfo(String userName, String passwd) {
		sipConfig.username = userName;
		sipConfig.passwd = passwd;
	}

	public boolean start(String userName, String passwd) {
		setLoginInfo(userName, passwd);
		return start();
	}

	private boolean start() {

		if (SIPConfig.LOG_ENABLE)
			Log.d("SIPEngine", "start()...");

		if (bStart && (callAgent != null) && (regAgent != null))
			return true;

		bRetryRDS = true;

		try {

			if (TextUtils.isEmpty(sipConfig.username) || TextUtils.isEmpty(sipConfig.passwd))
				return false;

			sipConfig.realm = getServer();

			SipStack.init(null);
			SipStack.debug_level = 0;
			SipStack.max_retransmission_timeout = 4000;
			SipStack.default_transport_protocols = new String[1];
			SipStack.default_transport_protocols[0] = SIPConfig.DEFAULT_PROTOCOL;
			SipStack.default_port = SIPConfig.DEFAULT_LOCALPORT;

			// TODO:
			SipStack.ua_info = getQUserAgent();

			/** Added by huah in 2012-04-15 for QQVoice */
			SipStack.qua_info = getQUserAgent();

			// SipStack.server_info = version;

			IpAddress.setLocalIpAddress();

			// Modified by huah in 2014-04-08
			int localPort = NetUtil.getRandomPort(SipStack.default_port);
			sipProvider = new SipProvider(IpAddress.localIpAddress, localPort);

			sipConfig.contact_url = getContactURL(sipConfig.username);

			if (SIPConfig.DEFAULT_FROMUSER.length() == 0) {
				sipConfig.from_url = sipConfig.username;
			}
			else {
				sipConfig.from_url = SIPConfig.DEFAULT_FROMUSER;
			}

			if (sipConfig.from_url.indexOf("@") < 0) {
				sipConfig.from_url += "@" + sipConfig.realm;
			}

			// added by mandrajg
			String icsi = null;
			if (sipConfig.mmtel == true) {
				icsi = "\"urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel\"";
			}

			callAgent = new CallAgent(sipProvider, this, sipConfig, voiceEngine);
			regAgent = new RegisterAgent(sipProvider, sipConfig.from_url, // modified
					sipConfig.contact_url, sipConfig.username, sipConfig.realm, sipConfig.passwd, this, sipConfig, sipConfig.qvalue, icsi, device_guid); // added by mandrajg zhh_li

			nCallAgentState = CallAgent.STATE_IDLE;
			listen();

			if (SIPConfig.LOG_ENABLE)
				Log.d("SIPEngine", "start() success！");
		}
		catch (Exception e) {
			e.printStackTrace();
			bStart = false;
			return false;
		}

		if ((callAgent == null) || (regAgent == null)) {
			bStart = false;
			if (SIPConfig.LOG_ENABLE)
				Log.d("SIPEngine", "start() failed！");
			return false;
		}

		bStart = true;

		return true;
	}

	public void stop() {

		keepAliveAfterTime(0);

		bStart = false;

		unRegister();

		if (regAgent != null)
			regAgent.halt();
		if (callAgent != null)
			callAgent.hangup();
		if (sipProvider != null)
			sipProvider.halt();
		// added by zhh_li 2015-12-01 for kickout
		kickOutDialog = null;

	}

	public boolean reStart() {

		stop();
		start();

		return register();
	}

	public boolean reStart(String userName, String passwd) {

		stop();
		start();

		return register();
	}

	public void listen() {
		if (callAgent == null)
			callAgent = new CallAgent(sipProvider, this, sipConfig, voiceEngine);
		callAgent.listen();
	}

	public int getCallState() {
		return nCallState;
	}

	public synchronized boolean register(String userName, String passwd) {
		if (TextUtils.isEmpty(userName) || TextUtils.isEmpty(passwd))
			return false;

		sipConfig.username = userName;
		sipConfig.passwd = passwd;

		return register();
	}

	public synchronized boolean register() {
		if (SIPConfig.LOG_ENABLE)
			Log.d("SIPEngine", "register()...");

		reportStrBuffer = null;

		/*if (!NetworkUtil.isAvailable(context))
			return false;*/

		if (!bStart || (regAgent == null)) {
			if (start() == false)
				return false;
		}

		boolean result = false;

		try {
			if (regAgent != null && regAgent.register()) {
				// wakeLock(true);
				addLogInfo("start register...");
				if (SIPConfig.LOG_ENABLE)
					Log.d("SIPEngine", "SIP Reging...");
				if (!isOnline())
					nState = STATE_REGING;
				else
					nState = STATE_REFRESHING;
				result = true;
			}
		}
		catch (Exception ex) {
			// wakeLock(false);
			result = false;
		}

		if (result == false) {
			keepAliveAfterTime(sipConfig.refresh_time);
		}

		return result;
	}

	public boolean isOnline() {
		return ((nState == STATE_REFRESHING) || (nState == STATE_ONLINE));
	}

	public void unRegister() {

		keepAliveAfterTime(0);

		nState = STATE_OFFLINE;

		if (regAgent != null)
			regAgent.unregister();

	}

	public void keepAliveAfterTime(int time) {
		if (bStart) {
			if (sipListener != null)
				sipListener.onKeepAlive(time);
		}
	}

	public void keepAliveAfterTime(int time, boolean force) {
		if (force) {
			if (sipListener != null)
				sipListener.onKeepAlive(time);
		}
		else
			keepAliveAfterTime(time);
	}

	// Modified by huah in 2013-06-29
	public int call(String number) {
		int result = -1;

		if ((callAgent != null) || (start() == true)) {
			if ((TextUtils.isEmpty(number) == false) && (nCallState == CALLSTATE_IDLE)) {
				try {
					
						strCallNumber = number;

						String target = Util.adjustNumber(number);

						boolean ret = callAgent.call(target, false);
						if (ret) {
							nCallState = CALLSTATE_OUTGOING;
							result = 0;
						}
					
				}
				catch (Exception e) {
					e.printStackTrace();
				}
			}
		}

		if (result != 0) {
			onCallEnd();
		}

		return result;
	}

	/**
	 * 客户端主动挂断电话
	 * 
	 * @param isNotifyUI
	 *            是否通知UI层
	 * @author xiaofei 2014-2-26
	 */
	public void endCall(boolean isNotifyUI) {

		voiceEngine.reset();
		voiceEngine.ringback(false);

		nCallState = CALLSTATE_IDLE;
		if (callAgent != null)
			callAgent.hangup();
		listen();

		if (sipListener != null && isNotifyUI)
			sipListener.onCallEnd(SIPConfig.RR_OK);
	}

	/**
	 *  在程序退出或重置密码时检查当前通话新状态是否处理空闲状态，如果不是，将通话挂断
	 * @author xiaofei 2014-2-26
	 */
	public void checkAndEndCall() {
		if (nCallState != CALLSTATE_IDLE) {
			endCall(true);
		}
	}

	public void answerCall() {
		if (nCallState != CALLSTATE_INCOMING)
			return;

		if (callAgent == null)
			callAgent = new CallAgent(sipProvider, this, sipConfig, voiceEngine);
		if (callAgent.accept())
			nCallState = CALLSTATE_ON;
	}

	public void sendDTMF(char key) {
		if (nCallState != CALLSTATE_ON)
			return;
		if (callAgent != null)
			callAgent.info(key, 250);
	}

	private String getQUserAgent() {

		String uid =  sipConfig.uid;

		String devInfo = Util.getDevInfo(sipConfig.getContext());

		String localIP = IpAddress.localIpAddress;// NetUtil.getLocalIpAddress();

		String QUserAgent = uid + "*" + "@" + devInfo + "*" + 3 + "*" + "*" + localIP;

		return QUserAgent;
	}

	private String getContactURL(String username) {
		int i = username.indexOf("@");
		if (i != -1) {
			// if the username already contains a @
			// strip it and everthing following it
			username = username.substring(0, i);
		}

		return username + "@" + IpAddress.localIpAddress + (sipProvider.getPort() != 0 ? ":" + sipProvider.getPort() : "") + ";transport=" + sipProvider.getDefaultTransport();
	}

	public String getServer() {
		String server = SIPConfig.MASTER_SERVER;
		boolean isMaster = true;
		String curServerDomain = "";
		int curServerPort = SIPConfig.MASTER_SERVERPORT;
		if (TextUtils.isEmpty(curServerDomain) || !TextUtils.equals(curServerDomain, SIPConfig.SLAVE_SERVERDOMAIN)) {
			curServerDomain = SIPConfig.MASTER_SERVERDOMAIN;
			curServerPort = SIPConfig.MASTER_SERVERPORT;

		}
		if (TextUtils.equals(curServerDomain, SIPConfig.SLAVE_SERVERDOMAIN)) {
			isMaster = false;
			curServerPort = SIPConfig.SLAVE_SERVERPORT;
		}
		SocketAddress serverAddr;
		try {
			serverAddr = new SocketAddress(IpAddress.getByName(curServerDomain), curServerPort);
			server = serverAddr.toString();
		}
		catch (Exception e) {
			e.printStackTrace();
			if (isMaster)
				server = SIPConfig.MASTER_SERVER;
			else
				server = SIPConfig.SLAVE_SERVER;
		}
		catch (Error e) {
			e.printStackTrace();
			if (isMaster)
				server = SIPConfig.MASTER_SERVER;
			else
				server = SIPConfig.SLAVE_SERVER;
		}
		return server;
	}

	public void resetServer() {
		String curServerDomain = "";
		if (TextUtils.isEmpty(curServerDomain) || TextUtils.equals(curServerDomain, SIPConfig.SLAVE_SERVERDOMAIN)) {
			
		}
		else {
			
		}
	}

	@Override
	public void onRegisterSuccess() {
		// TODO Auto-generated method stub

		reportStrBuffer = null;

		nState = STATE_ONLINE;

		if (sipListener != null)
			sipListener.onRegisterOK();
		
		// added by zhh_li 2015-11-30 for kickout
		/** 注册成功后创建 kickOutDialog*/
		kickOutDialog = new KickOutDialog(sipProvider,this);

		keepAliveAfterTime(sipConfig.refresh_time);

		// wakeLock(false);

	}

	@Override
	public void onRegisterFailure(int code) {
		// TODO Auto-generated method stub
		nState = STATE_OFFLINE;
		if (sipListener != null)
			sipListener.onRegisterError(code);
		if(code != 403)
		{
			keepAliveAfterTime(sipConfig.refresh_time);
		}
		StringBuffer strBuf = new StringBuffer();
		strBuf.append("SIP register failed,Error Code:" + code);
		strBuf.append("\n");
		if (reportStrBuffer != null)
			strBuf.append(reportStrBuffer);

		// wakeLock(false);
	}

	@Override
	public void onRetryRegister(int code) {
		// TODO Auto-generated method stub
		if (bRetryRDS) {
			resetServer();
			stop();
			if (!start()) {
				onRegisterFailure(code);
				return;
			}
			bRetryRDS = false;
			if (!register()) {
				onRegisterFailure(code);
				return;
			}
			bRetryRDS = false;
		}
	}

	@Override
	public void onStatusChanged(int state, String number) {
		changeStatus(state, number);
	}

	/** Changes the call state */
	private synchronized void changeStatus(int state, String number) {
		if (nCallAgentState != state) {
			nCallAgentState = state;
			if (nCallAgentState == CallAgent.STATE_IDLE)
				listen();

		}
	}

	@Override
	public void onCallIncoming(String number) {
		// TODO Auto-generated method stub
		if (nCallState != CALLSTATE_IDLE)
			return;

		strCallNumber = number;

		nCallState = CALLSTATE_INCOMING;

		if (sipListener != null)
			sipListener.onCallIn(strCallNumber);
	}

	@Override
	public void onCallModifying() {
		// TODO Auto-generated method stub

	}

	@Override
	public void onCallRinging() {
		// TODO Auto-generated method stub
		if (nCallState != CALLSTATE_OUTGOING)
			return;

		nCallState = CALLSTATE_RINGING;

		if (sipListener != null)
			sipListener.onCallRing();
	}

	@Override
	public boolean onCallAccepted() {
		// TODO Auto-generated method stub
		if ((nCallState != CALLSTATE_OUTGOING) && (nCallState != CALLSTATE_RINGING))
			return false;
		nCallState = CALLSTATE_ON;
		if (sipListener != null)
			sipListener.onCallOK();
		return true;
	}

	@Override
	public void onCallRefused(int releaseReason) {
		// TODO Auto-generated method stub
		if ((nCallState != CALLSTATE_OUTGOING) && (nCallState != CALLSTATE_RINGING))
			return;

		voiceEngine.reset();

		nCallState = CALLSTATE_IDLE;

		if (sipListener != null)
			sipListener.onCallEnd(releaseReason);

		listen();

	}

	@Override
	public void onCallConfirmed() {
		// TODO Auto-generated method stub

	}

	@Override
	public void onCallTimeout() {
		// TODO Auto-generated method stub
		voiceEngine.reset();

		nCallState = CALLSTATE_IDLE;

		if (sipListener != null)
			sipListener.onCallEnd(SIPConfig.RR_TIMEOUT);

		listen();

	}

	@Override
	public void onCallCanceling() {
		// TODO Auto-generated method stub
		if (nCallState != CALLSTATE_INCOMING)
			return;

		voiceEngine.reset();

		nCallState = CALLSTATE_IDLE;

		if (sipListener != null)
			sipListener.onCallEnd(SIPConfig.RR_OK);

		listen();
	}

	@Override
	public void onCallClosing() {
		// TODO Auto-generated method stub
		if (nCallState != CALLSTATE_ON)
			return;

		voiceEngine.reset();

		nCallState = CALLSTATE_IDLE;

		if (sipListener != null)
			sipListener.onCallEnd(SIPConfig.RR_OK);

		listen();
	}

	@Override
	public void onCallClosed() {
		// TODO Auto-generated method stub
		voiceEngine.reset();

	}

	// Added by huah in 2012-09-01
	@Override
	public void onCallEnd() {
		// TODO Auto-generated method stub
		voiceEngine.reset();

		nCallState = CALLSTATE_IDLE;

		if (sipListener != null)
			sipListener.onCallEnd(SIPConfig.RR_OK);

		listen();
	}

	// public synchronized void wakeLock(boolean lock)
	// {
	// if (lock) {
	// if (pwLock == null)
	// {
	// PowerManager pm = (PowerManager) sipConfig.getContext().getSystemService(Context.POWER_SERVICE);
	// pwLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "uCaller.SIPCore");
	// pwLock.acquire();
	// }
	// } else if (pwLock != null)
	// {
	// if(pwLock.isHeld())
	// pwLock.release();
	// pwLock = null;
	// }
	// }

	public synchronized void addLogInfo(String str) {
		if (reportStrBuffer == null)
			reportStrBuffer = new StringBuffer();
		if (mFormatter == null)
			mFormatter = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss:SSS");
		String logTime = mFormatter.format(new Date(System.currentTimeMillis()));
		reportStrBuffer.append(logTime + "  ");
		reportStrBuffer.append(str);
		reportStrBuffer.append("\n");
	}
	public SipProvider getsp(){
		return sipProvider;
	}

	@Override
	public void onKickOut(Message message) {
		// TODO Auto-generated method stub
		DeviceGuidHeader deviceGuidHeader = message.getDeviceGuidHeader();
		String deviceGuid = deviceGuidHeader.getValue();
		if (deviceGuid.equalsIgnoreCase(SIPConfig.KEY_DEVICE_GUID))
			sipListener.onSIPEvent(SIPEngine.STATUS_KICK);
	}
	
	// added by zhh_li 2015-12-01 for kickout
	/*@Override
	public void onKickOut(Message msg) {
		DeviceGuidHeader deviceGuidHeader = msg.getDeviceGuidHeader();
		String deviceGuid = deviceGuidHeader.getValue();
		if (deviceGuid.equalsIgnoreCase(PreferencesUtil.getString(SIPConfig.KEY_DEVICE_GUID))) 
			sipListener.onSIPEvent(SIPEngine.STATUS_KICK);
	}

	@Override
	public void onKickOut(Message message) {
		// TODO Auto-generated method stub
		
	}*/
}
